#include "SQLBinaryOpExpr.h"
#include "SQLBinaryOperator.h"
#include "SQLBinaryOpExprGroup.h"
#include "SQLExprUtils.h"
#include "SQLVariantRefExpr.h"
#include "../../../utils/instanceof.h"
#include "../../visitor/ParameterizedVisitor.h"

// string_ptr SQLBinaryOpExpr::class_ = make_string_ptr("SQLBinaryOpExpr");
SETCALSS(SQLBinaryOpExpr);

SQLBinaryOpExpr::SQLBinaryOpExpr() : left(NULL), right(NULL), xoperator(NULL)
{
}
SQLBinaryOpExpr::~SQLBinaryOpExpr()
{

  // std::cout << __FILE__ << ": " << __LINE__ << ", in SQLBinaryOpExpr::~SQLBinaryOpExpr, step 1 " << std::endl;
  // if (left)
  // {
  // 	delete left;
  // 	left = 0;
  // }

  // if (right != NULL)
  // {
  // 	delete right;
  // 	right = NULL;
  // }

  // if (xoperator != NULL)
  //{
  // delete xoperator;
  // xoperator = NULL;

  //}

  // std::cout << __FILE__ << ": " << __LINE__ << ", in SQLBinaryOpExpr::~SQLBinaryOpExpr, step 2 " << std::endl;
}
SQLBinaryOpExpr::SQLBinaryOpExpr(SQLExpr_ptr theLeft, SQLBinaryOperator_ptr theXoperator, SQLExpr_ptr theRight)
{

  setLeft(theLeft);
  setRight(theRight);
  this->xoperator = theXoperator;
}

SQLBinaryOpExpr::SQLBinaryOpExpr(SQLExpr_ptr theLeft, SQLBinaryOperator_ptr theXoperator, SQLExpr_ptr theRight, DbType_ptr dbtype)
{

  setLeft(theLeft);
  setRight(theRight);
  this->xoperator = theXoperator;
}

SQLBinaryOpExpr::SQLBinaryOpExpr(SQLExpr_ptr theLeft, SQLExpr_ptr theRight, SQLBinaryOperator_ptr theXoperator)
{

  setLeft(theLeft);
  setRight(theRight);
  this->xoperator = theXoperator;
}

SQLExpr_ptr SQLBinaryOpExpr::getLeft()
{
  return this->left;
}

void SQLBinaryOpExpr::setLeft(SQLExpr_ptr theLeft)
{
  if (theLeft != NULL)
  {
    theLeft->setParent(SharedObject(SQLBinaryOpExpr));
  }
  this->left = theLeft;
}

SQLExpr_ptr SQLBinaryOpExpr::getRight()
{
  return this->right;
}

void SQLBinaryOpExpr::setRight(SQLExpr_ptr theRight)
{
  if (theRight != NULL)
  {
    theRight->setParent(SharedObject(SQLBinaryOpExpr));
  }
  this->right = theRight;
}

SQLBinaryOperator_ptr SQLBinaryOpExpr::getOperator()
{
  return this->xoperator;
}

void SQLBinaryOpExpr::setOperator(SQLBinaryOperator_ptr theXoperator)
{
  this->xoperator = theXoperator;
}

void SQLBinaryOpExpr::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLBinaryOpExpr)))
  {
    acceptChild(visitor, this->left);
    acceptChild(visitor, this->right);
  }

  visitor->endVisit(SharedObject(SQLBinaryOpExpr));
}

int SQLBinaryOpExpr::hashCode()
{
  // int prime = 31;
  // int result = 1;
  // result = prime * result + ((left == NULL) ? 0 : left.hashCode());
  // result = prime * result + ((xoperator == NULL) ? 0 : xoperator.hashCode());
  // result = prime * result + ((right == NULL) ? 0 : right.hashCode());
  // return result;
}

bool SQLBinaryOpExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == NULL)
  {
    return false;
  }
  if (!(instanceof <Object, SQLBinaryOpExpr>(obj)))
  {
    return false;
  }
  SQLBinaryOpExpr_ptr other = std::dynamic_pointer_cast<SQLBinaryOpExpr>(obj);
  if (left == NULL)
  {
    if (other->left != NULL)
    {
      return false;
    }
  }
  else if (!left->equals(other->left))
  {
    return false;
  }
  if (xoperator != other->xoperator)
  {
    return false;
  }
  if (right == NULL)
  {
    if (other->right != NULL)
    {
      return false;
    }
  }
  else if (!right->equals(other->right))
  {
    return false;
  }
  return true;
}

SQLExpr_ptr SQLBinaryOpExpr::and_(SQLExpr_ptr a, SQLExpr_ptr b)
{
  if (a == NULL)
  {
    return b;
  }

  if (b == NULL)
  {
    return a;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(a))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(a);
    if (group->getOperator() == SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::BooleanAnd)))
    {
      group->add(b);
      return group;
    }
    else if (group->getOperator() == SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::BooleanOr)) &&
             group->getItems()->size() == 1)
    {
      auto it = group->getItems()->begin();
      std::advance(it, 0);
      a = std::dynamic_pointer_cast<SQLExpr>((*it)->clone());
    }
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(b))
  {
    SQLBinaryOpExpr_ptr bb = std::dynamic_pointer_cast<SQLBinaryOpExpr>(b);
    if (bb->xoperator->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str())
    {
      return and_(and_(a, bb->left), bb->right);
    }
  }
  // else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(b))
  // {
  // 	SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup)b;
  // 	if (group.getOperator() == SQLBinaryOperator::BooleanOr && group.getItems().size() == 1)
  // 	{
  // 		std::list<SQLCaseExpr_Item *>::iterator it = group.getItems().begin();
  // 		std::advance(it,0);
  // 		b = (*it)->clone();
  // 	}
  // }

  // if (instanceof <SQLExpr, SQLBinaryOpExpr>(a) && instanceof <SQLExpr, SQLBinaryOpExprGroup>(b) && ((SQLBinaryOpExprGroup)b).getOperator() == SQLBinaryOperator::BooleanAnd)
  // {
  // 	SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup)b;
  // 	group.add(0, a);
  // 	return group;
  // }

  return SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(a, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::BooleanAnd)), b));
}

SQLExpr_list_ptr SQLBinaryOpExpr::split(SQLBinaryOpExpr_ptr x)
{
  return split(x, x->getOperator());
}

SQLExpr_list_ptr SQLBinaryOpExpr::split(SQLExpr_ptr x, SQLBinaryOperator_ptr op)
{
  // if (x instanceof SQLBinaryOpExprGroup)
  // {
  // 	SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup)x;
  // 	if (group.getOperator() == op)
  // 	{
  // 		return new ArrayList<SQLExpr>(group.getItems());
  // 	}
  // } else
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(x))
  {
    return split(std::dynamic_pointer_cast<SQLBinaryOpExpr>(x), op);
  }

  SQLExpr_list_ptr list = std::make_shared<std::list<SQLExpr_ptr>>();
  list->push_back(x);
  return list;
}

SQLExpr_list_ptr SQLBinaryOpExpr::split(SQLBinaryOpExpr_ptr x, SQLBinaryOperator_ptr op)
{
  if (x->getOperator()->name != op->name)
  {
    SQLExpr_list_ptr groupList = std::make_shared<std::list<SQLExpr_ptr>>();
    groupList->push_back((x));
    return groupList;
  }

  SQLExpr_list_ptr groupList = std::make_shared<std::list<SQLExpr_ptr>>();
  split(groupList, (x), op);
  return groupList;
}

void SQLBinaryOpExpr::split(SQLExpr_list_ptr outList, SQLExpr_ptr expr, SQLBinaryOperator_ptr op)
{
  if (expr == NULL)
  {
    return;
  }

  if (!(instanceof <SQLExpr, SQLBinaryOpExpr>(expr)))
  {
    outList->push_back(expr);
    return;
  }

  SQLBinaryOpExpr_ptr binaryExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr);

  if (binaryExpr->getOperator() != op)
  {
    outList->push_back(binaryExpr);
    return;
  }

  SQLExpr_list_ptr rightList = std::make_shared<std::list<SQLExpr_ptr>>();
  rightList->push_back(binaryExpr->getRight());
  for (SQLExpr_ptr left = binaryExpr->getLeft();;)
  {
    if (instanceof <SQLExpr, SQLBinaryOpExpr>(left))
    {
      SQLBinaryOpExpr_ptr leftBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(left);
      if (leftBinary->xoperator == op)
      {
        left = leftBinary->getLeft();
        rightList->push_back(leftBinary->getRight());
      }
      else
      {
        outList->push_back(leftBinary);
        break;
      }
    }
    else
    {
      outList->push_back(left);
      break;
    }
  }

  for (int i = rightList->size() - 1; i >= 0; --i)
  {
    std::list<SQLExpr_ptr>::iterator it = rightList->begin();
    std::advance(it, i);
    SQLExpr_ptr right = (*it);

    if (instanceof <SQLExpr, SQLBinaryOpExpr>(right))
    {
      SQLBinaryOpExpr_ptr binaryRight = std::dynamic_pointer_cast<SQLBinaryOpExpr>(right);
      if (binaryRight->xoperator == op)
      {
        {
          SQLExpr_ptr rightLeft = binaryRight->getLeft();
          if (instanceof <SQLExpr, SQLBinaryOpExpr>(rightLeft))
          {
            SQLBinaryOpExpr_ptr rightLeftBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(rightLeft);
            if (rightLeftBinary->xoperator == op)
            {
              split(outList, rightLeftBinary, op);
            }
            else
            {
              outList->push_back(rightLeftBinary);
            }
          }
          else
          {
            outList->push_back(rightLeft);
          }
        }
        {
          SQLExpr_ptr rightRight = binaryRight->getRight();
          if (instanceof <SQLExpr, SQLBinaryOpExpr>(rightRight))
          {
            SQLBinaryOpExpr_ptr rightRightBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(rightRight);
            if (rightRightBinary->xoperator == op)
            {
              split(outList, rightRightBinary, op);
            }
            else
            {
              outList->push_back(rightRightBinary);
            }
          }
          else
          {
            outList->push_back(rightRight);
          }
        }
      }
      else
      {
        outList->push_back(binaryRight);
      }
    }
    else
    {
      outList->push_back(right);
    }
  }
}

BOOL_ptr SQLBinaryOpExpr::isOr(SQLExpr_ptr x)
{
  return (instanceof <SQLExpr, SQLBinaryOpExpr>(x) && (std::dynamic_pointer_cast<SQLBinaryOpExpr>(x))->getOperator()->name == SQLBinaryOperator::BooleanOr.name)?BOOL::TRUE:BOOL::FALSE;
}

SQLExpr_ptr SQLBinaryOpExpr::or_(SQLExpr_ptr a, SQLExpr_ptr b)
{
  if (a == NULL)
  {
    return b;
  }

  if (b == NULL)
  {
    return a;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(a))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(a);
    if (group->getOperator()->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
    {
      group->add(b);
      return group;
    }
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(b))
  {
    SQLBinaryOpExpr_ptr bb = std::dynamic_pointer_cast<SQLBinaryOpExpr>(b);
    if (bb->xoperator->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
    {
      return or_(or_(a, bb->left), bb->right);
    }
  }

  return SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(a, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::BooleanOr)), b));
}

SQLExpr_ptr SQLBinaryOpExpr::other(SQLExpr_ptr x)
{
  if (x == left)
  {
    return right;
  }

  if (x == right)
  {
    return left;
  }

  return nullptr;
}

SQLBinaryOpExpr_ptr SQLBinaryOpExpr::merge(ParameterizedVisitor_ptr v, SQLBinaryOpExpr_ptr x)
{
  SQLObject_ptr parent = x->parent;

  for (;;)
  {
    if (instanceof <SQLExpr, SQLBinaryOpExpr>(x->right))
    {
      SQLBinaryOpExpr_ptr rightBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(x->right);
      if (instanceof <SQLExpr, SQLBinaryOpExpr>(x->left))
      {
        SQLBinaryOpExpr_ptr leftBinaryExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(x->left);
        if (SQLExprUtils::equals(leftBinaryExpr->right, rightBinary))
        {
          x = leftBinaryExpr;
          v->incrementReplaceCunt();
          continue;
        }
      }
      SQLExpr_ptr mergedRight = merge(v, rightBinary);
      if (mergedRight != x->right)
      {
        x = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(x->left, x->xoperator, mergedRight));
        v->incrementReplaceCunt();
      }

      x->setParent(parent);
    }

    break;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(x->left))
  {
    SQLExpr_ptr mergedLeft = merge(v, std::dynamic_pointer_cast<SQLBinaryOpExpr>(x->left));
    if (mergedLeft != x->left)
    {
      SQLBinaryOpExpr_ptr tmp = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(mergedLeft, x->xoperator, x->right));
      tmp->setParent(parent);
      x = tmp;
      v->incrementReplaceCunt();
    }
  }

  // ID = ? OR ID = ? => ID = ?
  if (x->xoperator->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str() &&
      !v->isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeInList))))
  {
    if ((instanceof <SQLExpr, SQLBinaryOpExpr>(x->left)) &&
        (instanceof <SQLExpr, SQLBinaryOpExpr>(x->right)))
    {
      SQLBinaryOpExpr_ptr leftBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(x->left);
      SQLBinaryOpExpr_ptr rightBinary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(x->right);

      if (mergeEqual(leftBinary, rightBinary))
      {
        v->incrementReplaceCunt();
        leftBinary->setParent(x->parent);
        leftBinary->addMergedItem(rightBinary);
        return leftBinary;
      }

      if (SQLExprUtils::isLiteralExpr(leftBinary->left) //
          && leftBinary->xoperator->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
      {
        if (mergeEqual(leftBinary->right, x->right))
        {
          v->incrementReplaceCunt();
          leftBinary->addMergedItem(rightBinary);
          return leftBinary;
        }
      }
    }
  }

  return x;
}

BOOL_ptr SQLBinaryOpExpr::mergeEqual(SQLExpr_ptr a, SQLExpr_ptr b)
{
  if (!(instanceof <SQLExpr, SQLBinaryOpExpr>(a)))
  {
    return BOOL::FALSE;
  }
  if (!(instanceof <SQLExpr, SQLBinaryOpExpr>(b)))
  {
    return BOOL::FALSE;
  }

  SQLBinaryOpExpr_ptr binaryA = std::dynamic_pointer_cast<SQLBinaryOpExpr>(a);
  SQLBinaryOpExpr_ptr binaryB = std::dynamic_pointer_cast<SQLBinaryOpExpr>(b);

  if (binaryA->xoperator->name->c_str() != SQLBinaryOperator::Equality.name->c_str())
  {
    return BOOL::FALSE;
  }

  if (binaryB->xoperator->name->c_str() != SQLBinaryOperator::Equality.name->c_str())
  {
    return BOOL::FALSE;
  }

  if (!(instanceof <SQLExpr, SQLLiteralExpr>(binaryA->right) ||
        instanceof <SQLExpr, SQLVariantRefExpr>(binaryA->right)))
  {
    return BOOL::FALSE;
  }

  if (!(instanceof <SQLExpr, SQLLiteralExpr>(binaryB->right) ||
        instanceof <SQLExpr, SQLVariantRefExpr>(binaryB->right)))
  {
    return BOOL::FALSE;
  }

  return (binaryA->left->equals(binaryB->left))?BOOL::TRUE:BOOL::FALSE;
}

void SQLBinaryOpExpr::addMergedItem(SQLBinaryOpExpr_ptr item)
{
  if (mergedList == nullptr)
  {
    mergedList = std::make_shared<std::list<SQLObject_ptr>>();
  }
  mergedList->push_back(item);
}